export class RingBuffer<T> {
  private list: (T | undefined)[]
  private startIdx: number = 0
  private maxSize: number
  private size: number

  public get length(): number {
    return this.size
  }

  constructor(maxSize: number) {
    if (maxSize <= 0) {
      throw Error('max size of ring buffer should larger than 0')
    }
    this.list = new Array(maxSize)
    this.maxSize = maxSize

    this.size = 0
  }

  public get(idx: number): T | undefined {
    if (idx < 0 || idx >= this.size) {
      return undefined
    }
    idx = (this.startIdx + idx) % this.maxSize
    return this.list[idx]
  }

  public set(idx: number, t: T): boolean {
    if (idx < 0 || idx >= this.size) {
      return false
    }
    idx = (this.startIdx + idx) % this.maxSize
    this.list[idx] = t
    return true
  }

  public push(e: T) {
    const idx = (this.startIdx + this.size) % this.maxSize
    this.list[idx] = e
    this.size++
    this.adjustSize()
  }

  public truncate(idx: number) {
    if (idx < 0 || idx >= this.size) {
      return
    }
    this.size = idx
  }

  private adjustSize() {
    if (this.size > this.maxSize) {
      const diff = this.size - this.maxSize
      this.startIdx = (this.startIdx + diff) % this.maxSize
      this.size = this.maxSize
    }
  }
}
